package com.kuangstudy.controller.list;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.kuangstudy.entity.Content;
import com.kuangstudy.service.list.PushContentService;
import com.kuangstudy.service.set.FollowerServicee;
import com.kuangstudy.vo.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @description:
 * @author: xuke
 * @time: 2021/6/11 23:34
 */
@RestController
@Slf4j
public class PullContentController {


    @Autowired
    private PushContentService pushContentService;
    @Autowired
    private RedisTemplate redisTemplate;

    private final static String MY_WEIBO_CONTENT_ZSET_LIST = "weibo:content:zset:";
    private final static String MY_WEIBO_ATTENTION_ZSET_LIST = "weibo:attention:zset:";

    /*
     * @Author xuke
     * @Description 发送微博
     * @Date 23:49 2021/6/11
     * @Param [content]
     * @return com.kuangstudy.vo.R
     **/
    @PostMapping("/pull/sendmsg")
    public R pullpost(@RequestBody Content content) {
        // 1: 发送微博
        Content content1 = pushContentService.saveContent(content);
        // 2: 发送微博后，把消息加入到我的个人微博列表zset
        this.addMyWeiboZset(content1);
        return R.ok().data("content1", content1);
    }

    // 1--------------------发送微博后，把消息加入到我的个人微博列表zset
    public void addMyWeiboZset(Content content) {
        // 1: 指定个人微博列表的key
        String key = MY_WEIBO_CONTENT_ZSET_LIST + content.getUserId();
        // 2:把当前时间作为zset的score用来排序和方便关注者获取
        // 按秒来换算单位
        long score = System.currentTimeMillis() / 1000;
        this.redisTemplate.opsForZSet().add(key, content.getId(), score);
    }

    // 1--------------------获取个人主页列表
    @GetMapping("/pull/myhomeindex")
    public Page<Content> myweibohomelist(Integer userId, int page, int size) {
        Page<Content> page1 = new Page<>(page, size);

        // 1: 计算zset分页的起始和结束
        int start = (page - 1) * size;
        int end = start + size - 1;
        try {
            // 2: 指定个人微博列表的key
            String key = MY_WEIBO_CONTENT_ZSET_LIST + userId;
            // 3: 查询用户发微博的总数
            int total = this.redisTemplate.opsForZSet().zCard(key).intValue();
            page1.setTotal(total);
            // 4: 查看每页显示的内容 使用redis的zrevrange
            Set<ZSetOperations.TypedTuple<Integer>> rangeWithScores = this.redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
            List<Integer> rangesList = new ArrayList<>();
            for (ZSetOperations.TypedTuple<Integer> rangeWithScore : rangeWithScores) {
                rangesList.add(rangeWithScore.getValue());
            }
            page1.setRecords(getcontents(rangesList));
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return page1;
    }

    // 2---------------------获取关注列表
    @GetMapping("/pull/attentionList")
    public Page<Content> attentionList(Integer userid, int page, int size) {
        // 如果是第一页，刷新关注集合
        if (page == 1) {
            this.refreshAttentionWeiboList(userid);
        }

        Page<Content> page1 = new Page<>(page, size);

        // 1: 计算zset分页的起始和结束
        int start = (page - 1) * size;
        int end = start + size - 1;
        try {
            //2：读取用户userid(飞哥)的关注用户列表
            String attentionkey = MY_WEIBO_ATTENTION_ZSET_LIST + userid;

            //3: 设置总数
            long total = this.redisTemplate.opsForZSet().zCard(attentionkey);
            page1.setTotal(total);

            // 4：分页查询
            List<Integer> list = new ArrayList<>();
            Set<ZSetOperations.TypedTuple<Integer>> rangeWithScores = this.redisTemplate.opsForZSet().reverseRangeWithScores(attentionkey, start, end);
            for (ZSetOperations.TypedTuple<Integer> rangeWithScore : rangeWithScores) {
                list.add(rangeWithScore.getValue());
                log.info("关注post集合value={},score={}", rangeWithScore.getValue(), rangeWithScore.getScore());
            }

            // 5：去拿明细数据
            List<Content> contents = this.getcontents(list);
            page1.setRecords(contents);

        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }

        return page1;
    }

    // 2--------------------刷新拉取用户关注列表 ,一般是：用户第一刷新或定时触发
    public void refreshAttentionWeiboList(Integer userid) {
        // 获取刷新时间
        String refreshkey = "weibo:user:refresh:time:" + userid;
        Long ago = Long.valueOf(String.valueOf(this.redisTemplate.opsForValue().get(refreshkey)));
        // 如果时间为空，取两天前的时间
        if (ago == null) {
            // 获取当前时间
            long now = System.currentTimeMillis() / 1000;
            // 当前时间减去两天
            ago = now - 60 * 60 * 24 * 2;
        }

        // 读取该用户的关注列表
        String followerkey = FollowerServicee.FOLLOWEE_SET_KEY + userid;
        Set<Integer> members = this.redisTemplate.opsForSet().members(followerkey);
        log.info("用户:{},的关注列表有：{}", userid, members);
        // 对当前时间
        long now = System.currentTimeMillis() / 1000;
        // 对关注的人进行列表循环，开始把关注人的微博信息放入到我的zset的个人首页的列表集合中
        String my_attention_key = MY_WEIBO_ATTENTION_ZSET_LIST + userid;
        for (Integer memberid : members) {
            // 把关注每个用户的微博列表信息获取出来
            String ckey = MY_WEIBO_CONTENT_ZSET_LIST + memberid;
            Set<ZSetOperations.TypedTuple<Integer>> range = this.redisTemplate.opsForZSet().rangeByScoreWithScores(ckey, ago, now);
            if (!CollectionUtils.isEmpty(range)) {
                // 加入我的关注post集合,就是通过上次刷新的时间计算出最新的微博，写入首页zset集合中，再更新刷新时间
                this.redisTemplate.opsForZSet().add(my_attention_key, range);
            }
        }
        // 把最后更新的放入到缓存中，以便下次进行获取有个时间参考，然后下次根据这个时候作为起始查询时间
        this.redisTemplate.opsForValue().set(refreshkey, now);
        // 关注post集合，最多留存1000个。
        // 1: 计算post集合总数，
        long total = this.redisTemplate.opsForZSet().zCard(my_attention_key);
        // 如果total大于 1000，就剔除多余的post
        if (total > 1000) {
            long end = total - 1000;
            this.redisTemplate.opsForZSet().removeRange(my_attention_key, 0, end);
        }

        // 过期的目的是指：如果用户30天内都不访问微博，代表不是活跃或者最新的数据，一直放在内存中，消耗服务器资源，所以30天不访问
        // 直接清空、
        long days = this.redisTemplate.getExpire(my_attention_key, TimeUnit.DAYS);
        // 如果用户连续10天都没有访问的话就开始进入倒计时30天过期
        if (days < 10) {
            // 设置30天过期
            this.redisTemplate.expire(my_attention_key, 30, TimeUnit.DAYS);
        }
    }

    // 根据存储的文章id到hash中去查询明细
    List<Content> getcontents(List<Integer> postids) {
        List<Content> contentList = new ArrayList<>();

        // 要获取hash的字段值
        List<String> hashKeys = new ArrayList<>();
        hashKeys.add("id");
        hashKeys.add("content");
        hashKeys.add("userId");
        hashKeys.add("nickname");
        hashKeys.add("sendtime");

        HashOperations<String, String, Object> hashOperations = this.redisTemplate.opsForHash();
        for (Integer postid : postids) {
            String key = "weibo:content:" + postid;
            List<Object> objects = hashOperations.multiGet(key, hashKeys);
            if (objects != null && objects.size() > 0) {
                if (objects.get(0) == null && objects.get(1) == null) {
                    // 去db查询
//                    Content content = contentService.getById(postid);
//                    contentList.add(content);
                } else {
                    Content content = new Content();
                    content.setId(postid);
                    content.setContent(String.valueOf(objects.get(1)));
                    content.setUserId(Integer.valueOf(objects.get(2).toString()));
                    content.setNickname(String.valueOf(objects.get(3)));
                    content.setSendtime(Long.valueOf(objects.get(4).toString()));
                    contentList.add(content);
                }
            }
        }

        return contentList;
    }
}
